import abc
import datetime

from coin.exchange.base.kr_rest.py_product import PyProduct
from coin.base.datetime_util import to_timestamp_int
import coin.exchange.base.kr_rest.level_book as level_book_mdl
from coin.exchange.base.kr_rest.product import NativeProduct
from coin.exchange.base.kr_rest.futures_product import NativeFuturesProduct
from coin.proto.coin_market_query_pb2 import ProductTicker, ExchangeTicker, ProductKline


class PublicClientBase(metaclass=abc.ABCMeta):
  def query_ticker(self, product):
    assert isinstance(product, (NativeProduct, NativeFuturesProduct, PyProduct))
    update = self.query_ticker_impl(product)
    assert isinstance(update.msg, ProductTicker)
    return update

  def query_exchange_ticker(self):
    update = self.query_exchange_ticker_impl()
    assert isinstance(update.msg, ExchangeTicker)
    if not update.msg.HasField('query_ts'):
      update.msg.query_ts = int(update.req_timestamp * 10**9)
    return update

  def query_level_book(self, product):
    assert isinstance(product, (NativeProduct, NativeFuturesProduct))
    update = self.query_level_book_impl(product)
    assert isinstance(update.msg, level_book_mdl.LevelBook)
    return update

  def query_history_kline(self, product, kline_period, start_time, end_time):
    assert isinstance(product, (NativeProduct, NativeFuturesProduct, PyProduct))
    assert isinstance(start_time, datetime.datetime)
    assert isinstance(end_time, datetime.datetime)
    assert end_time >= start_time
    update = self.query_history_kline_impl(product, kline_period, start_time, end_time)
    assert isinstance(update.msg, ProductKline)
    start_ts = to_timestamp_int(start_time)
    end_ts = to_timestamp_int(end_time)
    filtered = [
        kline for kline in update.msg.klines if
        start_ts <= kline.kline_timestamp < end_ts]
    update.msg.ClearField('klines')
    update.msg.klines.extend(filtered)
    klines = update.msg.klines
    assert len(klines) > 0
    for kline in klines:
      assert start_ts <= kline.kline_timestamp < end_ts, kline.kline_timestamp
    return update

  def query_total_kline(self, kline_period, size):
    assert isinstance(size, int) and size > 0
    update = self.query_total_kline_impl(kline_period, size)
    assert isinstance(update.msg, ProductKline)
    return update

  def query_ticker_impl(self, product):
    raise NotImplementedError()

  def query_history_kline_impl(self, product, kline_type, start_time, end_time):
    raise NotImplementedError()

  def query_total_kline_impl(self, exchange, kline_type, size):
    raise NotImplementedError()

  @abc.abstractmethod
  def query_level_book_impl(self, product):
    raise NotImplementedError()
